package com.example.mqttdevicesimulator;

import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

/**
 * Used for handling MQTT client operations, such as connecting, subscribing,
 * unsubscribing, and publishing.
 */
public class ClientOperation {

    private MqttAsyncClient client;
    private AppCompatActivity context;
    private String broker;
    private static final String TAG = "ClientOperation";

    public int maxTTW = 4000; //(ms) max time to wait in AsyncClient
    public final Object waiter = new Object(); // thread locker

    public ClientOperation(AppCompatActivity context, String broker) {
        this.context = context;
        this.broker = broker;
    }

    public MqttAsyncClient createClient() throws MqttException {
        MemoryPersistence persistence = new MemoryPersistence();

        client = new MqttAsyncClient(broker, UtilConstants.OtherConstants.getMqttClientId(), persistence);

        return client;
    }

    /**
     * Connect to an MQTT broker
     */
    public void doConnect() {
        MqttConnectOptions conOpt = new MqttConnectOptions();
        conOpt.setCleanSession(true);

        // connector call back listener:
        IMqttActionListener conListener = new IMqttActionListener() {
            @Override
            public void onSuccess(IMqttToken asyncActionToken) {
                Log.i(TAG, "Connect success");
                carryOn();
            }

            @Override
            public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                Log.i(TAG, "connect failed" + exception);
                Toast.makeText(context, "Connect failed ", Toast.LENGTH_SHORT).show();
                carryOn();
            }

            void carryOn() {
                synchronized (waiter) {
                    waiter.notifyAll();
                }

            }

        };
        try {
            client.connect(conOpt, conListener);
        } catch (MqttException e) {
            Log.e(TAG, "doConnect: fail to connect", e);
        }
        Log.i(TAG, "doConnect: connected to " + broker);
    }

    /**
     * Subscribe a topic
     *
     * @param topic topic that will be subscribed
     * @param qos   quality of service
     */
    public void doSubscribe(String topic, int qos) {
        try {
            IMqttToken subToken = client.subscribe(topic, qos);
            subToken.waitForCompletion();
            Log.i(TAG, "doSubscribe: subscribed " + topic);
        } catch (MqttException e) {
            Log.e(TAG, "doSubscribe: fail to subscribe " + topic, e);
        }
    }

    /**
     * Unsubscribe a topic
     *
     * @param topic topic that will be unsubscribed
     */
    public void doUnsubscribe(String topic) {
        try {
            IMqttToken subToken = client.unsubscribe(topic);
            subToken.waitForCompletion();
            Log.i(TAG, "doSubscribe: unsubscribed " + topic);
        } catch (MqttException e) {
            Log.e(TAG, "doSubscribe: fail to unsubscribe " + topic, e);
        }
    }

    /**
     * Publish an message to a topic
     *
     * @param topic   topic that will be sent message to
     * @param message MQTT message
     */
    public void doPublish(String topic, MqttMessage message) {
        try {
            IMqttToken pubToken = client.publish(topic, message);
            pubToken.waitForCompletion();
            Log.i(TAG, "doPublish to Topic: " + topic);
        } catch (MqttException e) {
            Log.e(TAG, "doPublish: fail to publish\n" + message.toString(), e);
        }
    }
}
